﻿using System;
using System.Collections.Generic;
using System.Text;
using NUnit.Framework;
using Langexplr.Abc;
using System.IO;
using Microsoft.FSharp.Reflection;

namespace AbcLibrary.Tests
{
    [TestFixture]
    public class BranchLabelIdentification : BaseClassTestFixture
    {
        [Test]
        public void TestSimpleIfStatement()
        {

            using (BinaryReader reader = new BinaryReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("AbcLibrary.Tests.filesForBranch.code.abc")))
            {
                AbcFileInstruction[] instructions = GetInitMethodInstructions(reader);

                Assert.AreEqual(29, instructions.Length,"Number of instructions");
                int i = 0;
                Assert.AreEqual("getlocal_0", instructions[i].Name); i++;
                Assert.AreEqual("pushscope", instructions[i].Name); i++;
                Assert.AreEqual("pushbyte", instructions[i].Name); i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name); i++;
                Assert.AreEqual("swap", instructions[i].Name); i++;
                Assert.AreEqual("setslot", instructions[i].Name); i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name); i++;
                Assert.AreEqual("getslot", instructions[i].Name); i++;
                Assert.AreEqual("pushbyte", instructions[i].Name); i++;
                Assert.AreEqual("ifngt", instructions[i].Name);

                Assert.IsTrue(instructions[i].IfNGt1.IsSolvedReference(),"If label solved");

                string ifTargetDestination = instructions[i].IfNGt1.SolvedReference1;
                i++;                
                Assert.AreEqual("findpropertystrict", instructions[i].Name); i++;
                Assert.AreEqual("pushstring", instructions[i].Name); i++;
                Assert.AreEqual("callprop", instructions[i].Name); i++;
                Assert.AreEqual("pop", instructions[i].Name); i++;
                Assert.AreEqual("jump", instructions[i].Name);

                Assert.IsTrue(instructions[i].Jump1.IsSolvedReference(), "Jump label solved");
                string jumpTargetDestination = instructions[i].Jump1.SolvedReference1;
                
                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name); 

                Assert.AreEqual(instructions[i].ArtificialCodeBranchLabel1, ifTargetDestination, "If target label");
                i++;

                Assert.AreEqual("findpropertystrict", instructions[i].Name); i++;
                Assert.AreEqual("pushstring", instructions[i].Name); i++;
                Assert.AreEqual("callprop", instructions[i].Name); i++;
                Assert.AreEqual("pop", instructions[i].Name); i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);
                Assert.AreEqual(instructions[i].ArtificialCodeBranchLabel1, jumpTargetDestination, "Jump target label");
                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name); i++;
                Assert.AreEqual("pushstring", instructions[i].Name); i++;
                Assert.AreEqual("callprop", instructions[i].Name); i++;
                Assert.AreEqual("coerce_a", instructions[i].Name); i++;
                Assert.AreEqual("setlocal_1", instructions[i].Name); i++;
                Assert.AreEqual("getlocal_1", instructions[i].Name); i++;
                Assert.AreEqual("returnvalue", instructions[i].Name); i++;
                Assert.AreEqual("kill", instructions[i].Name); i++;


            }

        }

        [Test]
        public void TestSimpleForStatement()
        {

            using (BinaryReader reader = new BinaryReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("AbcLibrary.Tests.filesForBranch.code2.abc")))
            {
                AbcFileInstruction[] instructions = GetInitMethodInstructions(reader);

                Assert.AreEqual(29, instructions.Length, "Number of instructions");
                int i = 0;

                Assert.AreEqual("getlocal_0", instructions[i].Name);
                i++;
                Assert.AreEqual("pushscope", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("swap", instructions[i].Name);
                i++;
                Assert.AreEqual("setslot", instructions[i].Name);
                i++;
                Assert.AreEqual("jump", instructions[i].Name);
                
                Assert.IsTrue(instructions[i].Jump1.IsSolvedReference(), "Jump label solved");
                string jumpTargetDestination = instructions[i].Jump1.SolvedReference1;
                
                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                string ifTargetDestination = instructions[i].ArtificialCodeBranchLabel1;

                i++;
                Assert.AreEqual("label", instructions[i].Name);
                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("callprop", instructions[i].Name);
                i++;
                Assert.AreEqual("pop", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("increment", instructions[i].Name);
                i++;
                Assert.AreEqual("setlocal_1", instructions[i].Name);
                i++;
                Assert.AreEqual("getlocal_1", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("swap", instructions[i].Name);
                i++;
                Assert.AreEqual("setslot", instructions[i].Name);
                i++;
                Assert.AreEqual("kill", instructions[i].Name);
                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(jumpTargetDestination, instructions[i].ArtificialCodeBranchLabel1, "Target");

                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("iflt", instructions[i].Name);

                Assert.IsTrue(instructions[i].IfLt1.IsSolvedReference(), "If label solved");
                Assert.AreEqual(ifTargetDestination, instructions[i].IfLt1.SolvedReference1, "Jump target label");
            

                i++;
                Assert.AreEqual("returnvoid", instructions[i].Name);
                i++;



            }

        }

        [Test]
        public void TestNestedIfStatements()
        {

            using (BinaryReader reader = new BinaryReader(System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("AbcLibrary.Tests.filesForBranch.code3.abc")))
            {
                AbcFileInstruction[] instructions = GetInitMethodInstructions(reader);

                Assert.AreEqual(43, instructions.Length, "Number of instructions");
                int i = 0;


                Assert.AreEqual("getlocal_0", instructions[i].Name);
                i++;
                Assert.AreEqual("pushscope", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("swap", instructions[i].Name);
                i++;
                Assert.AreEqual("setslot", instructions[i].Name);
                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("ifngt", instructions[i].Name);

                Assert.IsTrue(instructions[i].IfNGt1.IsSolvedReference());
                string if1Target = instructions[i].IfNGt1.SolvedReference1;

                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("ifnlt", instructions[i].Name);

                Assert.IsTrue(instructions[i].IfNLt1.IsSolvedReference());
                string if2Target = instructions[i].IfNLt1.SolvedReference1;

                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name);
                i++;
                Assert.AreEqual("pushstring", instructions[i].Name);
                i++;
                Assert.AreEqual("callprop", instructions[i].Name);
                i++;
                Assert.AreEqual("pop", instructions[i].Name);
                i++;
                Assert.AreEqual("jump", instructions[i].Name);

                Assert.IsTrue(instructions[i].Jump1.IsSolvedReference());
                string jump1Target = instructions[i].Jump1.SolvedReference1;

                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(if2Target, instructions[i].ArtificialCodeBranchLabel1,"First inner if target");

                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name);
                i++;
                Assert.AreEqual("pushstring", instructions[i].Name);
                i++;
                Assert.AreEqual("callprop", instructions[i].Name);
                i++;
                Assert.AreEqual("pop", instructions[i].Name);
                i++;

                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(jump1Target, instructions[i].ArtificialCodeBranchLabel1,"After first if");

                i++;

                Assert.AreEqual("jump", instructions[i].Name);
                
                Assert.IsTrue(instructions[i].Jump1.IsSolvedReference());
                string jump2Target = instructions[i].Jump1.SolvedReference1;

                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(if1Target, instructions[i].ArtificialCodeBranchLabel1, "Outer if else block");

                i++;
                Assert.AreEqual("getglobalscope", instructions[i].Name);
                i++;
                Assert.AreEqual("getslot", instructions[i].Name);
                i++;
                Assert.AreEqual("pushbyte", instructions[i].Name);
                i++;
                Assert.AreEqual("ifnlt", instructions[i].Name);
                Assert.IsTrue(instructions[i].IfNLt1.IsSolvedReference());
                string if3Target = instructions[i].IfNLt1.SolvedReference1;
                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name);
                i++;
                Assert.AreEqual("pushstring", instructions[i].Name);
                i++;
                Assert.AreEqual("callprop", instructions[i].Name);
                i++;
                Assert.AreEqual("pop", instructions[i].Name);
                i++;                
                Assert.AreEqual("jump", instructions[i].Name);

                Assert.IsTrue(instructions[i].Jump1.IsSolvedReference());
                string jump3Target = instructions[i].Jump1.SolvedReference1;

                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(if3Target,instructions[i].ArtificialCodeBranchLabel1);

                i++;
                Assert.AreEqual("findpropertystrict", instructions[i].Name);
                i++;
                Assert.AreEqual("pushstring", instructions[i].Name);
                i++;
                Assert.AreEqual("callprop", instructions[i].Name);
                i++;
                Assert.AreEqual("pop", instructions[i].Name);
                i++;
                Assert.AreEqual("ArtificialJumpLabel", instructions[i].Name);

                Assert.AreEqual(jump2Target, instructions[i].ArtificialCodeBranchLabel1, "After outer if");
                Assert.AreEqual(jump3Target, instructions[i].ArtificialCodeBranchLabel1, "After outer if");

                i++;
                Assert.AreEqual("returnvoid", instructions[i].Name);
                i++;

            }

        }
    }
}
